import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { GetcDto } from 'src/app/dto/getc.dto';
import { SelectDto } from 'src/app/dto/select.dto';
import { coursesMap } from './courses.map';
import { LocalStorageService } from '../local-storage/local-storage.service';
import { SelectedCourseResBody } from './interfaces/selected-courses-response-body.interface';
import { GetcResponseBody } from './interfaces/getc-responce-body.interfase';
import { SelectedCourse } from './interfaces/selected-course.interface';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  })
};

@Injectable({
  providedIn: 'root'
})
export class CourseService {
  private coursesMap = coursesMap;
  // tslint:disable-next-line: variable-name
  private _token: string;

  constructor(
    private http: HttpClient,
    private localStorage: LocalStorageService
  ) {
    this.token = this.localStorage.get('token');
  }

  get token(): string {
    return this._token;
  }

  set token(token: string) {
    this._token = token;
  }

  getToken(): string {
    return this.localStorage.get('token');
  }

  getCourse(getcDto: GetcDto) {
    return this.http.post<any>('/api/getc', getcDto, httpOptions).pipe();
  }

  /**
   * 压平数据
   */
  private flattenData(data: Array<object>) {
    const target = {};
    for (const value of data) {
      Object.assign(target, value);
    }
    return target;
  }

  /**
   * 整理数据
   */
  private disposalData(data: Array<object>) {
    const disposaledData = [];
    const flattenedData = this.flattenData(data);

    // 遍历所有的课程号
    for (const no in flattenedData) {
      if (flattenedData.hasOwnProperty(no)) {
        const residue = flattenedData[no];
        // 根据课程号，取得课程详情
        let details = this.coursesMap.get(Number(no));

        // 如果这个课程号，没有对应得课程详情
        if (details === undefined) {
          details = {
            name: 'null',
            time: 'null',
            teacher: 'null'
          };
        }

        // 整理成需要的表格数据
        disposaledData.push({
          no: Number(no),
          residue,
          ...details
        });
      }
    }

    return disposaledData;
  }

  getCourseData(getcDto: GetcDto): Promise<Array<object>> {
    return new Promise((resolve, reject) => {
      this.getCourse(getcDto).subscribe(
        data => {
          resolve(this.disposalData(data.cou_re));
        },
        error => {
          console.error(error);
          reject(error);
        }
      );
    });
  }

  // tslint:disable-next-line: variable-name
  selectCourse(cou_select: string[]) {
    return this.http
      .post<SelectedCourseResBody>(
        '/api/select',
        {
          token: this.getToken(),
          cou_select,
          cou_deselect: []
        } as SelectDto,
        httpOptions
      )
      .pipe();
  }

  // tslint:disable-next-line: variable-name
  cancelCourse(cou_deselect: string[]) {
    return this.http
      .post<SelectedCourseResBody>(
        '/api/select',
        {
          token: this.getToken(),
          cou_select: [],
          cou_deselect
        } as SelectDto,
        httpOptions
      )
      .pipe();
  }

  /**
   * 获取已选课程号
   */
  getSelectedCourseNumber(): Promise<number[]> {
    return new Promise<number[]>((resolve, reject) => {
      this.http
        .post<GetcResponseBody>('/api/getc', { token: this.token }, httpOptions)
        .subscribe(
          responseBody => {
            resolve(
              responseBody.cou_al
                .split(' ')
                .filter(value => value !== '')
                .map<number>(value => {
                  return Number(value);
                })
            );
          },
          error => {
            reject(error);
          }
        );
    });
  }

  /**
   * 获取已选的课程列表
   */
  async getListOfSelectedCourses(): Promise<SelectedCourse[]> {
    const selectedCourseNumber = await this.getSelectedCourseNumber();

    return selectedCourseNumber.map<SelectedCourse>(no => ({
      no,
      ...this.coursesMap.get(no)
    }));
  }
}
